package com.glodon.paas.sdk.document;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import org.apache.commons.lang.StringUtils;

import com.glodon.paas.document.api.bean.File;
import com.glodon.paas.document.api.bean.Share;
import com.glodon.paas.sdk.PaasClient;
import com.glodon.paas.sdk.SdkException;
import com.glodon.paas.storage.BulkStorage;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

/**
 * entry point for accessing Document REST API
 * @author guojf
 *
 */
public class Document {
	
	private final static String PATH_FILES = "/files";
	private final static String PATH_ROOT_FILES = "/files/root";
	private final static String PATH_FILE_BY_ID = "/files/id/{fileId}";
	private final static String PATH_FILE_DATA = "/files/id/{fileId}/data";
	private final static String PATH_FILE_DATA_BY_REVISION = "/files/id/{fileId}/data/{revision}";
	private final static String PATH_SUB_FILES = "/files/id/{fileId}/sub-files";
	private final static String PATH_FILE_BY_PARENT_NAME = "/files/id/{fileId}/sub-files/{fileName}";
	private final static String PATH_RECYCLER_FILE_BY_ID = "/files/recycler/id/{fileId}";
	private final static String PATH_RECYCLER = "/files/recycler";
	private final static String PATH_FILE_PATH = "/files/path/{filePath}";
	
	private final static String PATH_SHARE_BYME = "/share/byme";
	private final static String PATH_SHARE_FORME = "/share/forme";
	private final static String PATH_SHARE_FILE = "/share/id/{fileId}/email/{email}/privilege/{privilege}";
	private final static String PATH_SHARE_FILE_BYME = "/share/byme/id/{fileId}";
	private final static String PATH_SHARE_FILE_FORME = "/share/forme/id/{fileId}";
	private final static String PATH_SHARE_INFO = "/share/shareInfo/id/{fileId}";
	
	private PaasClient client;
	
	/**
	 * @param client PAAS客户端
	 */
	public Document(PaasClient client) {
		this.client = client;
	}
	
	/**
	 * @return PAAS客户端
	 */
	public PaasClient getPaasClient() {
		return client;
	}
	
	/** 设定PaasClient对象
	 * @param client PAAS客户端
	 */
	public void setPaasClient(PaasClient client) {
		this.client = client;
	}
	
	/**
	 * 根据path生成对应的uri地址
	 * @param path 相对路径
	 * @return URI
	 */
	public String endpoint(String path) {
		return client.getEndpointConfig().documentEndpoint(path).toString();
	}
	
	/**
	 * 获取根目录下文件信息
	 * @return json格式的字符串
	 * @throws SdkException
	 */
	public String getRootFilesAsJson() throws SdkException {
		return dealWithGet(PATH_ROOT_FILES).getEntity(String.class);
	}
	
	/**
	 * 获取根目录下文件信息
	 * @return 文件数组
	 * @throws SdkException
	 */
	public File[] getRootFiles() throws SdkException {
		return dealWithGet(PATH_ROOT_FILES).getEntity(File[].class);
	}
	
	/**
	 * 根据fileId获取文件元数据信息
	 * @param fileId 文件id
	 * @return json格式的字符串
	 * @throws SdkException
	 */
	public String getFileAsJson(String fileId) throws SdkException{
		return dealWithGet(PATH_FILE_BY_ID.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	/**
	 * 根据fileId获取文件元数据信息
	 * @param fileId 文件id
	 * @return 文件对象
	 * @throws SdkException
	 */
	public File getFile(String fileId) throws SdkException{
		return dealWithGet(PATH_FILE_BY_ID.replace("{fileId}", fileId)).getEntity(File.class);
	}
	
	/**
	 * 根据文件路径获取文件元数据信息
	 * @param path 文件的存放路径
	 * @return 文件对象
	 * @throws SdkException
	 */
	public File getFileByPath(String path) throws SdkException{
		return dealWithGet(PATH_FILE_PATH.replace("{filePath}", path)).getEntity(File.class);
	}
	
	/**
	 * 根据文件路径获取文件元数据信息
	 * @param path 文件的存放路径
	 * @return json格式的字符串
	 * @throws SdkException
	 */
	public String getFileByPathAsJson(String path) throws SdkException{
		return dealWithGet(PATH_FILE_PATH.replace("{filePath}", path)).getEntity(String.class);
	}
	
	
	/**
	 * 根据fileId获取子文件元数据信息
	 * @param fileId 文件id
	 * @return json格式的字符串
	 * @throws SdkException
	 */
	public String getSubFilesAsJson(String fileId) throws SdkException{
		return dealWithGet(PATH_SUB_FILES.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	/**
	 * 根据fileId获取子文件元 数据信息
	 * @param fileId 文件id
	 * @return 文件数组
	 * @throws SdkException
	 */
	public File[] getSubFiles(String fileId) throws SdkException{
		return dealWithGet(PATH_SUB_FILES.replace("{fileId}", fileId)).getEntity(File[].class);
	}
	
	/**
	 * 根据fileId获取最新可以版本的文件内容
	 * @param fileId 文件id
	 * @return 输入流
	 * @throws SdkException
	 */
	public InputStream getFileStream(String fileId) throws SdkException{
		String api = endpoint(PATH_FILE_DATA.replace("{fileId}", fileId));
		WebResource resource = client.signAndCreateResource(api, "GET");
		ClientResponse c = resource.type(MediaType.APPLICATION_OCTET_STREAM_TYPE).get(ClientResponse.class);
		if(c.getStatus() != 200){
			throw new SdkException(c.getEntity(String.class));
		}
		return c.getEntity(InputStream.class);
	}
	
	/**
	 * 根据fileId和revision，获取指定文件指定版本的文件内容
	 * @param fileId 文件id
	 * @param revision 文件版本号
	 * @return 输入流
	 * @throws SdkException
	 */
	public InputStream getFileStream(String fileId,String revision) throws SdkException{
		String api = endpoint(PATH_FILE_DATA_BY_REVISION.replace("{revision}", revision).replace("{fileId}", fileId));
		WebResource resource = client.signAndCreateResource(api, "GET");
		ClientResponse c = resource.type(MediaType.APPLICATION_OCTET_STREAM_TYPE).get(ClientResponse.class);
		if(c.getStatus() != 200){
			throw new SdkException(c.getEntity(String.class));
		}
		return c.getEntity(InputStream.class);
	}
	
	/**
	 * 上传文件
	 * @param fileName 文件名
	 * @param parentId 父目录id
	 * @param data 文件输入流
	 * @param dataLength 文件长度
	 * @param byPart 是否采用断点续传
	 * @return 上传成功后的文件元数据信息。
	 * @throws SdkException
	 * @throws IOException
	 */
	public String createFileAsJson(String fileName, String parentId, InputStream data, long dataLength, boolean multiPart) throws SdkException, IOException{
		ClientResponse c = uploadFile(fileName, parentId, data, dataLength,multiPart);
		if(c!=null){
			return c.getEntity(String.class);
		}
		return null;
	}
	
	/**
	 * 上传文件
	 * @param fileName 文件名
	 * @param parentId 父目录id
	 * @param data 文件输入流
	 * @param dataLength 文件长度
	 * @param byPart 是否采用断点续传
	 * @return 创建成功后的文件对象。
	 * @throws SdkException
	 * @throws IOException
	 */
	public File createFile(String fileName, String parentId, InputStream data, long dataLength, boolean multiPart) throws SdkException, IOException{
		ClientResponse c = uploadFile(fileName, parentId, data, dataLength,multiPart);
		if(c!=null){
			return c.getEntity(File.class);
		}
		return null;
	}
	
	private ClientResponse uploadFile(String fileName, String parentId, InputStream data, long dataLength,boolean multiPart) throws SdkException, IOException{
		ClientResponse c = null;
		String signedApi = null;
		WebResource resource = null;
		if(dataLength<getPartSize()){
			multiPart=false;
		}
		UriBuilder builder = UriBuilder.fromUri(PATH_FILES).matrixParam("parentId", parentId).matrixParam("name", fileName).matrixParam("length", dataLength);
		URI uri;
		File file=null;
		if(multiPart){
			Long index = 0L;
			if(StringUtils.isEmpty(parentId)){
				File[] files = this.getRootFiles();
				for(File f : files){
					if((StringUtils.isEmpty(f.getExtension()) && f.getName().equalsIgnoreCase(fileName))
							|| (f.getName()+"."+f.getExtension()).equalsIgnoreCase(fileName)){
						file=f;
						break;
					}
				}
			}
			else{
				c = dealWithGet(PATH_FILE_BY_PARENT_NAME.replace("{fileId}", parentId).replace("{fileName}", fileName).replaceAll("//", "/"));
				if(c.getStatus()==200){
					file=c.getEntity(File.class);
				}
			}
//			if(file!=null 
//	    			&& file.latestUnavailableRevision()!=null 
//	    			&& file.latestUnavailableRevision().getFileSize()==Long.valueOf(dataLength)
//	    			&& file.latestUnavailableRevision().getParts()!=null){
//				index = file.latestUnavailableRevision().getParts().size()*BulkStorage.BLOCK_SIZE;
//			}
			data.skip(index);
			int parts = (int)((dataLength-index)/getPartSize());
			if((dataLength-index)%getPartSize()>0){
				parts++;
			}
			uri = builder.matrixParam("chunk", "TRUE").build();
			byte[] b=new byte[(int)getPartSize()];
			for(;parts>0;parts--){
				if(parts==1 && dataLength%getPartSize()>0){
					b= new byte[(int)(dataLength%getPartSize())];
				}
				signedApi = client.signUrl(endpoint(uri.toString()), "POST");
				resource = Client.create().resource(signedApi);
				data.read(b);
				c = resource.type(MediaType.APPLICATION_OCTET_STREAM).post(ClientResponse.class,new ByteArrayInputStream(b));
				if(c.getStatus() != 200){
					throw new SdkException(c.getEntity(String.class));
				}
			}
		}
		else{
			uri = builder.matrixParam("chunk", "FALSE").build();
			signedApi = client.signUrl(endpoint(uri.toString()), "POST");
	    	resource = Client.create().resource(signedApi);
	    	c = resource.type(MediaType.APPLICATION_OCTET_STREAM).post(ClientResponse.class,data);
	    	if(c.getStatus() != 200){
				throw new SdkException(c.getEntity(String.class));
			}
		}
		return c;
	}
	
	/**
	 * 创建文件夹
	 * @param name 文件夹名称
	 * @param parentId 父文件夹ID
	 * @return 创建成功的文件夹元数据信息，json格式的字符串。
	 * @throws SdkException
	 */
	public String createFolderAsJson(String name, String parentId) throws SdkException{
		URI uri = UriBuilder.fromUri(PATH_FILES).matrixParam("parentId", parentId).matrixParam("name", name).build();
		WebResource resource = client.signAndCreateResource(endpoint(uri.toString()), "POST");
		ClientResponse c = resource.type(MediaType.APPLICATION_OCTET_STREAM).post(ClientResponse.class);
		if(c.getStatus() != 200){
			throw new SdkException(c.getEntity(String.class));
		}
		return c.getEntity(String.class);
	}
	
	/**
	 * 创建文件夹
	 * @param name 文件夹名称
	 * @param parentId 父文件夹ID
	 * @return 创建成功的文件夹元数据信息。
	 * @throws SdkException
	 */
	public File createFolder(String name, String parentId) throws SdkException{
		URI uri = UriBuilder.fromUri(PATH_FILES).matrixParam("parentId", parentId).matrixParam("name", name).build();
		WebResource resource = client.signAndCreateResource(endpoint(uri.toString()), "POST");
		ClientResponse c = resource.type(MediaType.APPLICATION_OCTET_STREAM).post(ClientResponse.class);
		if(c.getStatus() != 200){
			throw new SdkException(c.getEntity(String.class));
		}
		return c.getEntity(File.class);
	}
	
	/**
	 * 删除文件到回收站
	 * @param fileId 被删除文件的ID
	 * @throws SdkException
	 */
	public String delFileToRecycler(String fileId) throws SdkException{
		return dealWithDelete(PATH_FILE_BY_ID.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	/**
	 * 彻底删除文件
	 * @param fileId 被删除文件的ID
	 * @throws SdkException
	 */
	public String delete(String fileId) throws SdkException{
		return dealWithDelete(PATH_RECYCLER_FILE_BY_ID.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	/**
	 * 还原被删除文件
	 * @param fileId 被删除文件的ID
	 * @return 还原成功的文件元数据信息
	 * @throws SdkException
	 */
	public File revocer(String fileId) throws SdkException{
		return dealWithPost(PATH_RECYCLER_FILE_BY_ID.replace("{fileId}", fileId)).getEntity(File.class);
	}
	
	/**
	 * 还原被删除文件
	 * @param fileId 被删除文件的ID
	 * @return 还原成功的文件元数据信息，json格式的字符串
	 * @throws SdkException
	 */
	public String revocerAsJson(String fileId) throws SdkException{
		return dealWithPost(PATH_RECYCLER_FILE_BY_ID.replace("{fileId}", fileId)).getEntity(String.class);
	}

	/**
	 * 清空回收站
	 * @throws SdkException
	 */
	public String cleanRecycler() throws SdkException{
		return dealWithDelete(PATH_RECYCLER).getEntity(String.class);
	}

	/**
	 * 获取回收站里的指定文件
	 * @throws SdkException
	 * @return 回收站里的指定文件
	 */
	public File getFileInRecycler(String fileId) throws SdkException{
		return dealWithGet(PATH_RECYCLER_FILE_BY_ID.replace("{fileId}", fileId)).getEntity(File.class);
	}
	
	/**
	 * 获取回收站里的指定文件
	 * @throws SdkException
	 * @return 回收站里的指定文件
	 */
	public String getFileInRecyclerAsJson(String fileId) throws SdkException{
		return dealWithGet(PATH_RECYCLER_FILE_BY_ID.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	/**
	 * 获取回收站里的全部文件
	 * @throws SdkException
	 * @return 回收站里的全部文件
	 */
	public File[] getFilesInRecycler() throws SdkException{
		return dealWithGet(PATH_RECYCLER).getEntity(File[].class);
	}
	
	/**
	 * 获取回收站里的文件
	 * @throws SdkException
	 * @return 回收站里的文件
	 */
	public String getFilesInRecyclerAsJson() throws SdkException{
		return dealWithGet(PATH_RECYCLER).getEntity(String.class);
	}
	
	/**
	 * @param path
	 * @return
	 * @throws SdkException
	 */
	private ClientResponse dealWithGet(String path) throws SdkException{
		String api = endpoint(path);		
		WebResource resource = client.signAndCreateResource(api, "GET");
		ClientResponse c = resource.type(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);
		if(c.getStatus() != 200 && c.getStatus() != 204){
			throw new SdkException(c.getEntity(String.class));
		}
		return c;
	}
	
	/**
	 * @param path
	 * @return
	 * @throws SdkException
	 */
	private ClientResponse dealWithPost(String path) throws SdkException{
		String api = endpoint(path);
		WebResource resource = client.signAndCreateResource(api, "POST");
		ClientResponse c = resource.type(MediaType.APPLICATION_JSON_TYPE).post(ClientResponse.class);
		if(c.getStatus() != 200){
			throw new SdkException(c.getEntity(String.class));
		}
		return c;
	}
	
	/**
	 * @param path
	 * @return
	 * @throws SdkException
	 */
	private ClientResponse dealWithDelete(String path) throws SdkException{
		String api = endpoint(path);
		WebResource resource = client.signAndCreateResource(api, "DELETE");
		ClientResponse c = resource.type(MediaType.APPLICATION_JSON_TYPE).delete(ClientResponse.class);
		if(c.getStatus() != 200){
			throw new SdkException(c.getEntity(String.class));
		}
		return c;
	}
	
	public static long getPartSize(){
		return BulkStorage.BLOCK_SIZE;
	}
	
	public Share shareFile(String fileId, String email, String privelage) throws SdkException{
		return dealWithPost(PATH_SHARE_FILE.replace("{fileId}", fileId).replace("{email}", email).replace("privelage", privelage)).getEntity(Share.class);
	}
	
	public String shareFileAsJson(String fileId, String email, String privelage) throws SdkException{
		return dealWithPost(PATH_SHARE_FILE.replace("{fileId}", fileId).replace("{email}", email).replace("privelage", privelage)).getEntity(String.class);
	}
	
	public File[] rootSharedFileByMe() throws SdkException{
		return dealWithGet(PATH_SHARE_BYME).getEntity(File[].class);
	}
	
	public String rootSharedFileByMeAsJson() throws SdkException{
		return dealWithGet(PATH_SHARE_BYME).getEntity(String.class);
	}

	public File[] rootSharedFileForMe() throws SdkException{
		return dealWithGet(PATH_SHARE_FORME).getEntity(File[].class);
	}
	
	public String rootSharedFileForMeAsJson() throws SdkException{
		return dealWithGet(PATH_SHARE_FORME).getEntity(String.class);
	}
	
	public File[] getSharedFileByMe(String fileId) throws SdkException{
		return dealWithGet(PATH_SHARE_FILE_BYME.replace("{fileId}", fileId)).getEntity(File[].class);
	}
	
	public String getSharedFileByMeAsJson(String fileId) throws SdkException{
		return dealWithGet(PATH_SHARE_FILE_BYME.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	public File[] getSharedFileForMe(String fileId) throws SdkException{
		return dealWithGet(PATH_SHARE_FILE_FORME.replace("{fileId}", fileId)).getEntity(File[].class);
	}
	
	public String getSharedFileForMeAsJson(String fileId) throws SdkException{
		return dealWithGet(PATH_SHARE_FILE_FORME.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	public String delelteSharedFileByMe(String fileId) throws SdkException{
		return dealWithDelete(PATH_SHARE_FILE_BYME.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	public String delelteSharedFileForMe(String fileId) throws SdkException{
		return dealWithDelete(PATH_SHARE_FILE_FORME.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
	public String getSharedInfo(String fileId) throws SdkException{
		return dealWithGet(PATH_SHARE_INFO.replace("{fileId}", fileId)).getEntity(String.class);
	}
	
}
